package com.team.zhuoke.ui;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Join;
import android.graphics.Paint.Style;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Pair;
import android.widget.TextView;

import com.team.zhuoke.R;

import java.util.ArrayList;
import java.util.WeakHashMap;

public class MagicTextView extends TextView {

    private ArrayList<Shadow> outerShadows;
    private ArrayList<Shadow> innerShadows;

    private WeakHashMap<String, Pair<Canvas, Bitmap>> canvasStore;

    private Canvas tempCanvas;
    private Bitmap tempBitmap;

    private Drawable foregroundDrawable;

    private float strokeWidth;
    private Integer strokeColor;
    private Join strokeJoin;
    private float strokeMiter;

    private int[] lockedCompoundPadding;
    private boolean frozen = false;

    public MagicTextView(Context context) {
        super(context);
        init(null);
    }
    public MagicTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }
    public MagicTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs);
    }

    public void init(AttributeSet attrs){
        outerShadows = new ArrayList<Shadow>();
        innerShadows = new ArrayList<Shadow>();
        if(canvasStore == null){
            canvasStore = new WeakHashMap<String, Pair<Canvas, Bitmap>>();
        }

        if(attrs != null){
            TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MagicTextView);

            String typefaceName = a.getString( R.styleable.MagicTextView_typeface);
            if(typefaceName != null) {
                Typeface tf = Typeface.createFromAsset(getContext().getAssets(), String.format("fonts/%s.ttf", typefaceName));
                setTypeface(tf);
            }

            if(a.hasValue(R.styleable.MagicTextView_foreground)){
                Drawable foreground = a.getDrawable(R.styleable.MagicTextView_foreground);
                if(foreground != null){
                    this.setForegroundDrawable(foreground);
                }else{
                    this.setTextColor(a.getColor(R.styleable.MagicTextView_foreground, 0xff000000));
                }
            }

            if(a.hasValue(R.styleable.MagicTextView_innerShadowColor)){
                this.addInnerShadow(a.getFloat(R.styleable.MagicTextView_innerShadowRadius, 0),
                        a.getFloat(R.styleable.MagicTextView_innerShadowDx, 0),
                        a.getFloat(R.styleable.MagicTextView_innerShadowDy, 0),
                        a.getColor(R.styleable.MagicTextView_innerShadowColor, 0xff000000));
            }

            if(a.hasValue(R.styleable.MagicTextView_outerShadowColor)){
                this.addOuterShadow(a.getFloat(R.styleable.MagicTextView_outerShadowRadius, 0),
                        a.getFloat(R.styleable.MagicTextView_outerShadowDx, 0),
                        a.getFloat(R.styleable.MagicTextView_outerShadowDy, 0),
                        a.getColor(R.styleable.MagicTextView_outerShadowColor, 0xff000000));
            }

            if(a.hasValue(R.styleable.MagicTextView_strokecolor)){
                float strokeWidth = a.getFloat(R.styleable.MagicTextView_strokewidth, 1);
                int strokeColor = a.getColor(R.styleable.MagicTextView_strokecolor, 0xff000000);
                float strokeMiter = a.getFloat(R.styleable.MagicTextView_strokemiter, 10);
                Join strokeJoin = null;
                switch(a.getInt(R.styleable.MagicTextView_strokeJoinStyle, 0)){
                    case(0): strokeJoin = Join.MITER; break;
                    case(1): strokeJoin = Join.BEVEL; break;
                    case(2): strokeJoin = Join.ROUND; break;
                }
                this.setStroke(strokeWidth, strokeColor, strokeJoin, strokeMiter);
            }
        }
    }

    public void setStroke(float width, int color, Join join, float miter){
        strokeWidth = width;
        strokeColor = color;
        strokeJoin = join;
        strokeMiter = miter;
    }

    public void setStroke(float width, int color){
        setStroke(width, color, Join.MITER, 10);
    }

    public void addOuterShadow(float r, float dx, float dy, int color){
        if(r == 0){ r = 0.0001f; }
        outerShadows.add(new Shadow(r,dx,dy,color));
    }

    public void addInnerShadow(float r, float dx, float dy, int color){
        if(r == 0){ r = 0.0001f; }
        innerShadows.add(new Shadow(r,dx,dy,color));
    }

    public void clearInnerShadows(){
        innerShadows.clear();
    }

    public void clearOuterShadows(){
        outerShadows.clear();
    }

    public void setForegroundDrawable(Drawable d){
        this.foregroundDrawable = d;
    }

    public Drawable getForeground(){
        return this.foregroundDrawable == null ? this.foregroundDrawable : new ColorDrawable(this.getCurrentTextColor());
    }


    @Override
    public void onDraw(Canvas canvas){
        super.onDraw(canvas);

        freeze();
        Drawable restoreBackground = this.getBackground();
        Drawable[] restoreDrawables = this.getCompoundDrawables();
        int restoreColor = this.getCurrentTextColor();

        this.setCompoundDrawables(null,  null, null, null);

        for(Shadow shadow : outerShadows){
            this.setShadowLayer(shadow.r, shadow.dx, shadow.dy, shadow.color);
            super.onDraw(canvas);
        }
        this.setShadowLayer(0,0,0,0);
        this.setTextColor(restoreColor);

        if(this.foregroundDrawable != null && this.foregroundDrawable instanceof BitmapDrawable){
            generateTempCanvas();
            super.onDraw(tempCanvas);
            Paint paint = ((BitmapDrawable) this.foregroundDrawable).getPaint();
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
            this.foregroundDrawable.setBounds(canvas.getClipBounds());
            this.foregroundDrawable.draw(tempCanvas);
            canvas.drawBitmap(tempBitmap, 0, 0, null);
            tempCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        }

        if(strokeColor != null){
            TextPaint paint = this.getPaint();
//          paint.setTextAlign(Paint.Align.CENTER);
            paint.setStyle(Style.STROKE);
            paint.setStrokeJoin(strokeJoin);
            paint.setStrokeMiter(strokeMiter);
            this.setTextColor(strokeColor);
            paint.setStrokeWidth(strokeWidth);
            super.onDraw(canvas);
            paint.setStyle(Style.FILL);
            this.setTextColor(restoreColor);
        }
        if(innerShadows.size() > 0){
            generateTempCanvas();
            TextPaint paint = this.getPaint();
            for(Shadow shadow : innerShadows){
                this.setTextColor(shadow.color);
                super.onDraw(tempCanvas);
                this.setTextColor(0xFF000000);
                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
                paint.setMaskFilter(new BlurMaskFilter(shadow.r, BlurMaskFilter.Blur.NORMAL));

                tempCanvas.save();
                tempCanvas.translate(shadow.dx, shadow.dy);
                super.onDraw(tempCanvas);
                tempCanvas.restore();
                canvas.drawBitmap(tempBitmap, 0, 0, null);
                tempCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

                paint.setXfermode(null);
                paint.setMaskFilter(null);
                this.setTextColor(restoreColor);
                this.setShadowLayer(0,0,0,0);
            }
        }
        if(restoreDrawables != null){
            this.setCompoundDrawablesWithIntrinsicBounds(restoreDrawables[0], restoreDrawables[1], restoreDrawables[2], restoreDrawables[3]);
        }
        this.setBackgroundDrawable(restoreBackground);
        this.setTextColor(restoreColor);
        unfreeze();
    }

    private void generateTempCanvas(){
        String key = String.format("%dx%d", getWidth(), getHeight());
        Pair<Canvas, Bitmap> stored = canvasStore.get(key);
        if(stored != null){
            tempCanvas = stored.first;
            tempBitmap = stored.second;
        }else{
            tempCanvas = new Canvas();
            tempBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
            tempCanvas.setBitmap(tempBitmap);
            canvasStore.put(key, new Pair<Canvas, Bitmap>(tempCanvas, tempBitmap));
        }
    }

    public void freeze(){
        lockedCompoundPadding = new int[]{
                getCompoundPaddingLeft(),
                getCompoundPaddingRight(),
                getCompoundPaddingTop(),
                getCompoundPaddingBottom()
        };
        frozen = true;
    }

    public void unfreeze(){
        frozen = false;
    }


    @Override
    public void requestLayout(){
        if(!frozen) super.requestLayout();
    }

    @Override
    public void postInvalidate(){
        if(!frozen) super.postInvalidate();
    }

    @Override
    public void postInvalidate(int left, int top, int right, int bottom){
        if(!frozen) super.postInvalidate(left, top, right, bottom);
    }

    @Override
    public void invalidate(){
        if(!frozen) super.invalidate();
    }

    @Override
    public void invalidate(Rect rect){
        if(!frozen) super.invalidate(rect);
    }

    @Override
    public void invalidate(int l, int t, int r, int b){
        if(!frozen) super.invalidate(l,t,r,b);
    }

    @Override
    public int getCompoundPaddingLeft(){
        return !frozen ? super.getCompoundPaddingLeft() : lockedCompoundPadding[0];
    }

    @Override
    public int getCompoundPaddingRight(){
        return !frozen ? super.getCompoundPaddingRight() : lockedCompoundPadding[1];
    }

    @Override
    public int getCompoundPaddingTop(){
        return !frozen ? super.getCompoundPaddingTop() : lockedCompoundPadding[2];
    }

    @Override
    public int getCompoundPaddingBottom(){
        return !frozen ? super.getCompoundPaddingBottom() : lockedCompoundPadding[3];
    }

    public static class Shadow{
        float r;
        float dx;
        float dy;
        int color;
        public Shadow(float r, float dx, float dy, int color){
            this.r = r;
            this.dx = dx;
            this.dy = dy;
            this.color = color;
        }
    }
}